home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Resources / Chat & Communication / Digsby build 37 / digsby_setup.exe / lib / util / BeautifulSoup.pyo (.txt) < prev    next >
Python Compiled Bytecode  |  2008-10-13  |  46KB  |  1,596 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.5)
  3.  
  4. from __future__ import generators
  5. __author__ = 'Leonard Richardson (crummy.com)'
  6. __contributors__ = [
  7.     'Sam Ruby (intertwingly.net)',
  8.     'the unwitting Mark Pilgrim (diveintomark.org)',
  9.     'http://www.crummy.com/software/BeautifulSoup/AUTHORS.html']
  10. __version__ = '3.0.3'
  11. __copyright__ = 'Copyright (c) 2004-2006 Leonard Richardson'
  12. __license__ = 'PSF'
  13. from sgmllib import SGMLParser as _SGMLParser
  14.  
  15. class SGMLParser(_SGMLParser):
  16.     
  17.     def convert_charref(self, name):
  18.         
  19.         try:
  20.             n = int(name)
  21.         except ValueError:
  22.             return None
  23.  
  24.         if n <= n:
  25.             pass
  26.         elif not n <= 127:
  27.             return None
  28.         
  29.         return self.convert_codepoint(n)
  30.  
  31.  
  32. from sgmllib import SGMLParseError
  33. import codecs
  34. import types
  35. import re
  36. import sgmllib
  37. from htmlentitydefs import name2codepoint
  38. sgmllib.tagfind = re.compile('[a-zA-Z][-_.:a-zA-Z0-9]*')
  39. sgmllib.charref = re.compile('&#(\\d+|x[0-9a-fA-F]+);')
  40. DEFAULT_OUTPUT_ENCODING = 'utf-8'
  41.  
  42. class PageElement:
  43.     
  44.     def setup(self, parent = None, previous = None):
  45.         self.parent = parent
  46.         self.previous = previous
  47.         self.next = None
  48.         self.previousSibling = None
  49.         self.nextSibling = None
  50.         if self.parent and self.parent.contents:
  51.             self.previousSibling = self.parent.contents[-1]
  52.             self.previousSibling.nextSibling = self
  53.         
  54.  
  55.     
  56.     def replaceWith(self, replaceWith):
  57.         oldParent = self.parent
  58.         myIndex = self.parent.contents.index(self)
  59.         if hasattr(replaceWith, 'parent') and replaceWith.parent == self.parent:
  60.             index = self.parent.contents.index(replaceWith)
  61.             if index and index < myIndex:
  62.                 myIndex = myIndex - 1
  63.             
  64.         
  65.         self.extract()
  66.         oldParent.insert(myIndex, replaceWith)
  67.  
  68.     
  69.     def extract(self):
  70.         if self.parent:
  71.             
  72.             try:
  73.                 self.parent.contents.remove(self)
  74.             except ValueError:
  75.                 pass
  76.             except:
  77.                 None<EXCEPTION MATCH>ValueError
  78.             
  79.  
  80.         None<EXCEPTION MATCH>ValueError
  81.         lastChild = self._lastRecursiveChild()
  82.         nextElement = lastChild.next
  83.         if self.previous:
  84.             self.previous.next = nextElement
  85.         
  86.         if nextElement:
  87.             nextElement.previous = self.previous
  88.         
  89.         self.previous = None
  90.         lastChild.next = None
  91.         self.parent = None
  92.         if self.previousSibling:
  93.             self.previousSibling.nextSibling = self.nextSibling
  94.         
  95.         if self.nextSibling:
  96.             self.nextSibling.previousSibling = self.previousSibling
  97.         
  98.         self.previousSibling = None
  99.         self.nextSibling = None
  100.  
  101.     
  102.     def _lastRecursiveChild(self):
  103.         lastChild = self
  104.         while hasattr(lastChild, 'contents') and lastChild.contents:
  105.             lastChild = lastChild.contents[-1]
  106.         return lastChild
  107.  
  108.     
  109.     def insert(self, position, newChild):
  110.         if (isinstance(newChild, basestring) or isinstance(newChild, unicode)) and not isinstance(newChild, NavigableString):
  111.             newChild = NavigableString(newChild)
  112.         
  113.         position = min(position, len(self.contents))
  114.         if hasattr(newChild, 'parent') and newChild.parent != None:
  115.             if newChild.parent == self:
  116.                 index = self.find(newChild)
  117.                 if index and index < position:
  118.                     position = position - 1
  119.                 
  120.             
  121.             newChild.extract()
  122.         
  123.         newChild.parent = self
  124.         previousChild = None
  125.         if position == 0:
  126.             newChild.previousSibling = None
  127.             newChild.previous = self
  128.         else:
  129.             previousChild = self.contents[position - 1]
  130.             newChild.previousSibling = previousChild
  131.             newChild.previousSibling.nextSibling = newChild
  132.             newChild.previous = previousChild._lastRecursiveChild()
  133.         if newChild.previous:
  134.             newChild.previous.next = newChild
  135.         
  136.         newChildsLastElement = newChild._lastRecursiveChild()
  137.         if position >= len(self.contents):
  138.             newChild.nextSibling = None
  139.             parent = self
  140.             parentsNextSibling = None
  141.             while not parentsNextSibling:
  142.                 parentsNextSibling = parent.nextSibling
  143.                 parent = parent.parent
  144.                 if not parent:
  145.                     break
  146.                     continue
  147.             if parentsNextSibling:
  148.                 newChildsLastElement.next = parentsNextSibling
  149.             else:
  150.                 newChildsLastElement.next = None
  151.         else:
  152.             nextChild = self.contents[position]
  153.             newChild.nextSibling = nextChild
  154.             if newChild.nextSibling:
  155.                 newChild.nextSibling.previousSibling = newChild
  156.             
  157.             newChildsLastElement.next = nextChild
  158.         if newChildsLastElement.next:
  159.             newChildsLastElement.next.previous = newChildsLastElement
  160.         
  161.         self.contents.insert(position, newChild)
  162.  
  163.     
  164.     def findNext(self, name = None, attrs = { }, text = None, **kwargs):
  165.         return self._findOne(self.findAllNext, name, attrs, text, **kwargs)
  166.  
  167.     
  168.     def findAllNext(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  169.         return self._findAll(name, attrs, text, limit, self.nextGenerator)
  170.  
  171.     
  172.     def findNextSibling(self, name = None, attrs = { }, text = None, **kwargs):
  173.         return self._findOne(self.findNextSiblings, name, attrs, text, **kwargs)
  174.  
  175.     
  176.     def findNextSiblings(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  177.         return self._findAll(name, attrs, text, limit, self.nextSiblingGenerator, **kwargs)
  178.  
  179.     fetchNextSiblings = findNextSiblings
  180.     
  181.     def findPrevious(self, name = None, attrs = { }, text = None, **kwargs):
  182.         return self._findOne(self.findAllPrevious, name, attrs, text, **kwargs)
  183.  
  184.     
  185.     def findAllPrevious(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  186.         return self._findAll(name, attrs, text, limit, self.previousGenerator, **kwargs)
  187.  
  188.     fetchPrevious = findAllPrevious
  189.     
  190.     def findPreviousSibling(self, name = None, attrs = { }, text = None, **kwargs):
  191.         return self._findOne(self.findPreviousSiblings, name, attrs, text, **kwargs)
  192.  
  193.     
  194.     def findPreviousSiblings(self, name = None, attrs = { }, text = None, limit = None, **kwargs):
  195.         return self._findAll(name, attrs, text, limit, self.previousSiblingGenerator, **kwargs)
  196.  
  197.     fetchPreviousSiblings = findPreviousSiblings
  198.     
  199.     def findParent(self, name = None, attrs = { }, **kwargs):
  200.         r = None
  201.         l = self.findParents(name, attrs, 1)
  202.         if l:
  203.             r = l[0]
  204.         
  205.         return r
  206.  
  207.     
  208.     def findParents(self, name = None, attrs = { }, limit = None, **kwargs):
  209.         return self._findAll(name, attrs, None, limit, self.parentGenerator, **kwargs)
  210.  
  211.     fetchParents = findParents
  212.     
  213.     def _findOne(self, method, name, attrs, text, **kwargs):
  214.         r = None
  215.         l = method(name, attrs, text, 1, **kwargs)
  216.         if l:
  217.             r = l[0]
  218.         
  219.         return r
  220.  
  221.     
  222.     def _findAll(self, name, attrs, text, limit, generator, **kwargs):
  223.         if isinstance(name, SoupStrainer):
  224.             strainer = name
  225.         else:
  226.             strainer = SoupStrainer(name, attrs, text, **kwargs)
  227.         results = ResultSet(strainer)
  228.         g = generator()
  229.         while True:
  230.             
  231.             try:
  232.                 i = g.next()
  233.             except StopIteration:
  234.                 break
  235.  
  236.             if i:
  237.                 found = strainer.search(i)
  238.                 if found:
  239.                     results.append(found)
  240.                     if limit and len(results) >= limit:
  241.                         break
  242.                     
  243.                 
  244.             found
  245.         return results
  246.  
  247.     
  248.     def nextGenerator(self):
  249.         i = self
  250.         while i:
  251.             i = i.next
  252.             yield i
  253.  
  254.     
  255.     def nextSiblingGenerator(self):
  256.         i = self
  257.         while i:
  258.             i = i.nextSibling
  259.             yield i
  260.  
  261.     
  262.     def previousGenerator(self):
  263.         i = self
  264.         while i:
  265.             i = i.previous
  266.             yield i
  267.  
  268.     
  269.     def previousSiblingGenerator(self):
  270.         i = self
  271.         while i:
  272.             i = i.previousSibling
  273.             yield i
  274.  
  275.     
  276.     def parentGenerator(self):
  277.         i = self
  278.         while i:
  279.             i = i.parent
  280.             yield i
  281.  
  282.     
  283.     def substituteEncoding(self, str, encoding = None):
  284.         if not encoding:
  285.             pass
  286.         encoding = 'utf-8'
  287.         return str.replace('%SOUP-ENCODING%', encoding)
  288.  
  289.     
  290.     def toEncoding(self, s, encoding = None):
  291.         if isinstance(s, unicode):
  292.             if encoding:
  293.                 s = s.encode(encoding)
  294.             
  295.         elif isinstance(s, str):
  296.             if encoding:
  297.                 s = s.encode(encoding)
  298.             else:
  299.                 s = unicode(s)
  300.         elif encoding:
  301.             s = self.toEncoding(str(s), encoding)
  302.         else:
  303.             s = unicode(s)
  304.         return s
  305.  
  306.  
  307.  
  308. class NavigableString(unicode, PageElement):
  309.     
  310.     def __getattr__(self, attr):
  311.         if attr == 'string':
  312.             return self
  313.         else:
  314.             raise AttributeError, "'%s' object has no attribute '%s'" % (self.__class__.__name__, attr)
  315.  
  316.     
  317.     def __unicode__(self):
  318.         return __str__(self, None)
  319.  
  320.     
  321.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  322.         if encoding:
  323.             return self.encode(encoding)
  324.         else:
  325.             return self
  326.  
  327.  
  328.  
  329. class CData(NavigableString):
  330.     
  331.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  332.         return '<![CDATA[%s]]>' % NavigableString.__str__(self, encoding)
  333.  
  334.  
  335.  
  336. class ProcessingInstruction(NavigableString):
  337.     
  338.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  339.         output = self
  340.         if '%SOUP-ENCODING%' in output:
  341.             output = self.substituteEncoding(output, encoding)
  342.         
  343.         return '<?%s?>' % self.toEncoding(output, encoding)
  344.  
  345.  
  346.  
  347. class Comment(NavigableString):
  348.     
  349.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  350.         return '<!--%s-->' % NavigableString.__str__(self, encoding)
  351.  
  352.  
  353.  
  354. class Declaration(NavigableString):
  355.     
  356.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  357.         return '<!%s>' % NavigableString.__str__(self, encoding)
  358.  
  359.  
  360.  
  361. class Tag(PageElement):
  362.     XML_ENTITIES_TO_CHARS = {
  363.         'apos': "'",
  364.         'quot': '"',
  365.         'amp': '&',
  366.         'lt': '<',
  367.         'gt': '>' }
  368.     BARE_AMPERSAND = re.compile('&(?!#\\d+;|#x[0-9a-fA-F]+;|\\w+;)')
  369.     
  370.     def __init__(self, parser, name, attrs = None, parent = None, previous = None):
  371.         self.parserClass = parser.__class__
  372.         self.isSelfClosing = parser.isSelfClosingTag(name)
  373.         self.convertHTMLEntities = parser.convertHTMLEntities
  374.         self.name = name
  375.         if attrs == None:
  376.             attrs = []
  377.         
  378.         self.attrs = attrs
  379.         self.contents = []
  380.         self.setup(parent, previous)
  381.         self.hidden = False
  382.         self.containsSubstitutions = False
  383.  
  384.     
  385.     def get(self, key, default = None):
  386.         return self._getAttrMap().get(key, default)
  387.  
  388.     
  389.     def has_key(self, key):
  390.         return self._getAttrMap().has_key(key)
  391.  
  392.     
  393.     def __getitem__(self, key):
  394.         return self._getAttrMap()[key]
  395.  
  396.     
  397.     def __iter__(self):
  398.         return iter(self.contents)
  399.  
  400.     
  401.     def __len__(self):
  402.         return len(self.contents)
  403.  
  404.     
  405.     def __contains__(self, x):
  406.         return x in self.contents
  407.  
  408.     
  409.     def __nonzero__(self):
  410.         return True
  411.  
  412.     
  413.     def __setitem__(self, key, value):
  414.         self._getAttrMap()
  415.         self.attrMap[key] = value
  416.         found = False
  417.         for i in range(0, len(self.attrs)):
  418.             if self.attrs[i][0] == key:
  419.                 self.attrs[i] = (key, value)
  420.                 found = True
  421.                 continue
  422.         
  423.         if not found:
  424.             self.attrs.append((key, value))
  425.         
  426.         self._getAttrMap()[key] = value
  427.  
  428.     
  429.     def __delitem__(self, key):
  430.         for item in self.attrs:
  431.             if item[0] == key:
  432.                 self.attrs.remove(item)
  433.             
  434.             self._getAttrMap()
  435.             if self.attrMap.has_key(key):
  436.                 del self.attrMap[key]
  437.                 continue
  438.         
  439.  
  440.     
  441.     def __call__(self, *args, **kwargs):
  442.         return apply(self.findAll, args, kwargs)
  443.  
  444.     
  445.     def __getattr__(self, tag):
  446.         if len(tag) > 3 and tag.rfind('Tag') == len(tag) - 3:
  447.             return self.find(tag[:-3])
  448.         elif tag.find('__') != 0:
  449.             return self.find(tag)
  450.         
  451.  
  452.     
  453.     def __eq__(self, other):
  454.         if not hasattr(other, 'name') and not hasattr(other, 'attrs') and not hasattr(other, 'contents') and self.name != other.name and self.attrs != other.attrs or len(self) != len(other):
  455.             return False
  456.         
  457.         for i in range(0, len(self.contents)):
  458.             if self.contents[i] != other.contents[i]:
  459.                 return False
  460.                 continue
  461.         
  462.         return True
  463.  
  464.     
  465.     def __ne__(self, other):
  466.         return not (self == other)
  467.  
  468.     
  469.     def __repr__(self, encoding = DEFAULT_OUTPUT_ENCODING):
  470.         return self.__str__(encoding)
  471.  
  472.     
  473.     def __unicode__(self):
  474.         return self.__str__(None)
  475.  
  476.     
  477.     def _convertEntities(self, match):
  478.         x = match.group(1)
  479.         if x in name2codepoint:
  480.             return unichr(name2codepoint[x])
  481.         elif '&' + x + ';' in self.XML_ENTITIES_TO_CHARS:
  482.             return '&%s;' % x
  483.         else:
  484.             return '&%s;' % x
  485.  
  486.     
  487.     def __str__(self, encoding = DEFAULT_OUTPUT_ENCODING, prettyPrint = False, indentLevel = 0):
  488.         encodedName = self.toEncoding(self.name, encoding)
  489.         attrs = []
  490.         if self.attrs:
  491.             for key, val in self.attrs:
  492.                 fmt = '%s="%s"'
  493.                 if isString(val):
  494.                     if self.containsSubstitutions and '%SOUP-ENCODING%' in val:
  495.                         val = self.substituteEncoding(val, encoding)
  496.                     
  497.                     if '"' in val:
  498.                         if "'" in val:
  499.                             val = val.replace('"', '"')
  500.                         else:
  501.                             fmt = "%s='%s'"
  502.                     
  503.                     if self.convertHTMLEntities:
  504.                         val = re.sub('&(\\w+);', self._convertEntities, val)
  505.                     
  506.                     val = val.replace('<', '<').replace('>', '>')
  507.                     val = self.BARE_AMPERSAND.sub('&', val)
  508.                 
  509.                 attrs.append(fmt % (self.toEncoding(key, encoding), self.toEncoding(val, encoding)))
  510.             
  511.         
  512.         close = ''
  513.         closeTag = ''
  514.         if self.isSelfClosing:
  515.             close = ' /'
  516.         else:
  517.             closeTag = '</%s>' % encodedName
  518.         (indentTag, indentContents) = (0, 0)
  519.         if prettyPrint:
  520.             indentTag = indentLevel
  521.             space = ' ' * (indentTag - 1)
  522.             indentContents = indentTag + 1
  523.         
  524.         contents = self.renderContents(encoding, prettyPrint, indentContents)
  525.         if self.hidden:
  526.             s = contents
  527.         else:
  528.             s = []
  529.             attributeString = ''
  530.             if attrs:
  531.                 attributeString = ' ' + ' '.join(attrs)
  532.             
  533.             if prettyPrint:
  534.                 s.append(space)
  535.             
  536.             s.append('<%s%s%s>' % (encodedName, attributeString, close))
  537.             if prettyPrint:
  538.                 s.append('\n')
  539.             
  540.             s.append(contents)
  541.             if prettyPrint and contents and contents[-1] != '\n':
  542.                 s.append('\n')
  543.             
  544.             if prettyPrint and closeTag:
  545.                 s.append(space)
  546.             
  547.             s.append(closeTag)
  548.             if prettyPrint and closeTag and self.nextSibling:
  549.                 s.append('\n')
  550.             
  551.             s = ''.join(s)
  552.         return s
  553.  
  554.     
  555.     def prettify(self, encoding = DEFAULT_OUTPUT_ENCODING):
  556.         return self.__str__(encoding, True)
  557.  
  558.     
  559.     def renderContents(self, encoding = DEFAULT_OUTPUT_ENCODING, prettyPrint = False, indentLevel = 0):
  560.         s = []
  561.         for c in self:
  562.             text = None
  563.             if isinstance(c, NavigableString):
  564.                 text = c.__str__(encoding)
  565.             elif isinstance(c, Tag):
  566.                 s.append(c.__str__(encoding, prettyPrint, indentLevel))
  567.             
  568.             if text and prettyPrint:
  569.                 text = text.strip()
  570.             
  571.             if text:
  572.                 if prettyPrint:
  573.                     s.append(' ' * (indentLevel - 1))
  574.                 
  575.                 s.append(text)
  576.                 if prettyPrint:
  577.                     s.append('\n')
  578.                 
  579.             prettyPrint
  580.         
  581.         return ''.join(s)
  582.  
  583.     
  584.     def find(self, name = None, attrs = { }, recursive = True, text = None, **kwargs):
  585.         r = None
  586.         l = self.findAll(name, attrs, recursive, text, 1, **kwargs)
  587.         if l:
  588.             r = l[0]
  589.         
  590.         return r
  591.  
  592.     findChild = find
  593.     
  594.     def findAll(self, name = None, attrs = { }, recursive = True, text = None, limit = None, **kwargs):
  595.         generator = self.recursiveChildGenerator
  596.         if not recursive:
  597.             generator = self.childGenerator
  598.         
  599.         return self._findAll(name, attrs, text, limit, generator, **kwargs)
  600.  
  601.     findChildren = findAll
  602.     first = find
  603.     fetch = findAll
  604.     
  605.     def fetchText(self, text = None, recursive = True, limit = None):
  606.         return self.findAll(text = text, recursive = recursive, limit = limit)
  607.  
  608.     
  609.     def firstText(self, text = None, recursive = True):
  610.         return self.find(text = text, recursive = recursive)
  611.  
  612.     
  613.     def append(self, tag):
  614.         self.contents.append(tag)
  615.  
  616.     
  617.     def _getAttrMap(self):
  618.         if not getattr(self, 'attrMap'):
  619.             self.attrMap = { }
  620.             for key, value in self.attrs:
  621.                 self.attrMap[key] = value
  622.             
  623.         
  624.         return self.attrMap
  625.  
  626.     
  627.     def childGenerator(self):
  628.         for i in range(0, len(self.contents)):
  629.             yield self.contents[i]
  630.         
  631.         raise StopIteration
  632.  
  633.     
  634.     def recursiveChildGenerator(self):
  635.         stack = [
  636.             (self, 0)]
  637.         while stack:
  638.             (tag, start) = stack.pop()
  639.             if isinstance(tag, Tag):
  640.                 for i in range(start, len(tag.contents)):
  641.                     a = tag.contents[i]
  642.                     yield a
  643.                     if isinstance(a, Tag) and tag.contents:
  644.                         if i < len(tag.contents) - 1:
  645.                             stack.append((tag, i + 1))
  646.                         
  647.                         stack.append((a, 0))
  648.                         break
  649.                         continue
  650.                 
  651.         raise StopIteration
  652.  
  653.  
  654.  
  655. class SoupStrainer:
  656.     
  657.     def __init__(self, name = None, attrs = { }, text = None, **kwargs):
  658.         self.name = name
  659.         if isString(attrs):
  660.             kwargs['class'] = attrs
  661.             attrs = None
  662.         
  663.         if kwargs:
  664.             if attrs:
  665.                 attrs = attrs.copy()
  666.                 attrs.update(kwargs)
  667.             else:
  668.                 attrs = kwargs
  669.         
  670.         self.attrs = attrs
  671.         self.text = text
  672.  
  673.     
  674.     def __str__(self):
  675.         if self.text:
  676.             return self.text
  677.         else:
  678.             return '%s|%s' % (self.name, self.attrs)
  679.  
  680.     
  681.     def searchTag(self, markupName = None, markupAttrs = { }):
  682.         found = None
  683.         markup = None
  684.         if isinstance(markupName, Tag):
  685.             markup = markupName
  686.             markupAttrs = markup
  687.         
  688.         if callable(self.name):
  689.             pass
  690.         callFunctionWithTagData = not isinstance(markupName, Tag)
  691.         if not not (self.name) and callFunctionWithTagData:
  692.             if (markup or self._matches(markup, self.name) or not markup) and self._matches(markupName, self.name):
  693.                 if callFunctionWithTagData:
  694.                     match = self.name(markupName, markupAttrs)
  695.                 else:
  696.                     match = True
  697.                     markupAttrMap = None
  698.                     for attr, matchAgainst in self.attrs.items():
  699.                         if not markupAttrMap:
  700.                             if hasattr(markupAttrs, 'get'):
  701.                                 markupAttrMap = markupAttrs
  702.                             else:
  703.                                 markupAttrMap = { }
  704.                                 for k, v in markupAttrs:
  705.                                     markupAttrMap[k] = v
  706.                                 
  707.                         
  708.                         attrValue = markupAttrMap.get(attr)
  709.                         if not self._matches(attrValue, matchAgainst):
  710.                             match = False
  711.                             break
  712.                             continue
  713.                     
  714.                 if match:
  715.                     if markup:
  716.                         found = markup
  717.                     else:
  718.                         found = markupName
  719.                 
  720.             
  721.         return found
  722.  
  723.     
  724.     def search(self, markup):
  725.         found = None
  726.         if isList(markup) and not isinstance(markup, Tag):
  727.             for element in markup:
  728.                 if isinstance(element, NavigableString) and self.search(element):
  729.                     found = element
  730.                     break
  731.                     continue
  732.             
  733.         elif isinstance(markup, Tag):
  734.             if not self.text:
  735.                 found = self.searchTag(markup)
  736.             
  737.         elif isinstance(markup, NavigableString) or isString(markup):
  738.             if self._matches(markup, self.text):
  739.                 found = markup
  740.             
  741.         else:
  742.             raise Exception, "I don't know how to match against a %s" % markup.__class__
  743.         return found
  744.  
  745.     
  746.     def _matches(self, markup, matchAgainst):
  747.         result = False
  748.         if matchAgainst == True and type(matchAgainst) == types.BooleanType:
  749.             result = markup != None
  750.         elif callable(matchAgainst):
  751.             result = matchAgainst(markup)
  752.         elif isinstance(markup, Tag):
  753.             markup = markup.name
  754.         
  755.         if markup and not isString(markup):
  756.             markup = unicode(markup)
  757.         
  758.         if hasattr(matchAgainst, 'match'):
  759.             if markup:
  760.                 pass
  761.             result = matchAgainst.search(markup)
  762.         elif isList(matchAgainst):
  763.             result = markup in matchAgainst
  764.         elif hasattr(matchAgainst, 'items'):
  765.             result = markup.has_key(matchAgainst)
  766.         elif matchAgainst and isString(markup):
  767.             if isinstance(markup, unicode):
  768.                 matchAgainst = unicode(matchAgainst)
  769.             else:
  770.                 matchAgainst = str(matchAgainst)
  771.         
  772.         if not result:
  773.             result = matchAgainst == markup
  774.         
  775.         return result
  776.  
  777.  
  778.  
  779. class ResultSet(list):
  780.     
  781.     def __init__(self, source):
  782.         list.__init__([])
  783.         self.source = source
  784.  
  785.  
  786.  
  787. def isList(l):
  788.     if not hasattr(l, '__iter__'):
  789.         pass
  790.     return type(l) in (types.ListType, types.TupleType)
  791.  
  792.  
  793. def isString(s):
  794.     
  795.     try:
  796.         if not isinstance(s, unicode):
  797.             pass
  798.         return isinstance(s, basestring)
  799.     except NameError:
  800.         return isinstance(s, str)
  801.  
  802.  
  803.  
  804. def buildTagMap(default, *args):
  805.     built = { }
  806.     for portion in args:
  807.         if hasattr(portion, 'items'):
  808.             for k, v in portion.items():
  809.                 built[k] = v
  810.             
  811.         if isList(portion):
  812.             for k in portion:
  813.                 built[k] = default
  814.             
  815.         built[portion] = default
  816.     
  817.     return built
  818.  
  819.  
  820. class BeautifulStoneSoup(Tag, SGMLParser):
  821.     SELF_CLOSING_TAGS = { }
  822.     NESTABLE_TAGS = { }
  823.     RESET_NESTING_TAGS = { }
  824.     QUOTE_TAGS = { }
  825.     MARKUP_MASSAGE = [
  826.         (re.compile('(<[^<>]*)/>'), (lambda x: x.group(1) + ' />')),
  827.         (re.compile('<!\\s+([^<>]*)>'), (lambda x: '<!' + x.group(1) + '>'))]
  828.     ROOT_TAG_NAME = u'[document]'
  829.     HTML_ENTITIES = 'html'
  830.     XML_ENTITIES = 'xml'
  831.     ALL_ENTITIES = [
  832.         HTML_ENTITIES,
  833.         XML_ENTITIES]
  834.     
  835.     def __init__(self, markup = '', parseOnlyThese = None, fromEncoding = None, markupMassage = True, smartQuotesTo = XML_ENTITIES, convertEntities = None, selfClosingTags = None):
  836.         self.parseOnlyThese = parseOnlyThese
  837.         self.fromEncoding = fromEncoding
  838.         self.smartQuotesTo = smartQuotesTo
  839.         if convertEntities:
  840.             self.smartQuotesTo = None
  841.         
  842.         if isList(convertEntities):
  843.             self.convertHTMLEntities = self.HTML_ENTITIES in convertEntities
  844.             self.convertXMLEntities = self.XML_ENTITIES in convertEntities
  845.         else:
  846.             self.convertHTMLEntities = self.HTML_ENTITIES == convertEntities
  847.             self.convertXMLEntities = self.XML_ENTITIES == convertEntities
  848.         self.instanceSelfClosingTags = buildTagMap(None, selfClosingTags)
  849.         SGMLParser.__init__(self)
  850.         if hasattr(markup, 'read'):
  851.             markup = markup.read()
  852.         
  853.         self.markup = markup
  854.         self.markupMassage = markupMassage
  855.         
  856.         try:
  857.             self._feed()
  858.         except StopParsing:
  859.             pass
  860.  
  861.         self.markup = None
  862.  
  863.     
  864.     def _feed(self, inDocumentEncoding = None):
  865.         markup = self.markup
  866.         if isinstance(markup, unicode):
  867.             if not hasattr(self, 'originalEncoding'):
  868.                 self.originalEncoding = None
  869.             
  870.         else:
  871.             dammit = UnicodeDammit(markup, [
  872.                 self.fromEncoding,
  873.                 inDocumentEncoding], smartQuotesTo = self.smartQuotesTo)
  874.             markup = dammit.unicode
  875.             self.originalEncoding = dammit.originalEncoding
  876.         if markup:
  877.             if self.markupMassage:
  878.                 if not isList(self.markupMassage):
  879.                     self.markupMassage = self.MARKUP_MASSAGE
  880.                 
  881.                 for fix, m in self.markupMassage:
  882.                     markup = fix.sub(m, markup)
  883.                 
  884.             
  885.         
  886.         self.reset()
  887.         if not markup:
  888.             pass
  889.         SGMLParser.feed(self, '')
  890.         SGMLParser.close(self)
  891.         self.endData()
  892.         while self.currentTag.name != self.ROOT_TAG_NAME:
  893.             self.popTag()
  894.  
  895.     
  896.     def __getattr__(self, methodName):
  897.         if methodName.find('start_') == 0 and methodName.find('end_') == 0 or methodName.find('do_') == 0:
  898.             return SGMLParser.__getattr__(self, methodName)
  899.         elif methodName.find('__') != 0:
  900.             return Tag.__getattr__(self, methodName)
  901.         else:
  902.             raise AttributeError
  903.  
  904.     
  905.     def isSelfClosingTag(self, name):
  906.         if not self.SELF_CLOSING_TAGS.has_key(name):
  907.             pass
  908.         return self.instanceSelfClosingTags.has_key(name)
  909.  
  910.     
  911.     def reset(self):
  912.         Tag.__init__(self, self, self.ROOT_TAG_NAME)
  913.         self.hidden = 1
  914.         SGMLParser.reset(self)
  915.         self.currentData = []
  916.         self.currentTag = None
  917.         self.tagStack = []
  918.         self.quoteStack = []
  919.         self.pushTag(self)
  920.  
  921.     
  922.     def popTag(self):
  923.         tag = self.tagStack.pop()
  924.         if len(self.currentTag.contents) == 1 and isinstance(self.currentTag.contents[0], NavigableString):
  925.             self.currentTag.string = self.currentTag.contents[0]
  926.         
  927.         if self.tagStack:
  928.             self.currentTag = self.tagStack[-1]
  929.         
  930.         return self.currentTag
  931.  
  932.     
  933.     def pushTag(self, tag):
  934.         if self.currentTag:
  935.             self.currentTag.append(tag)
  936.         
  937.         self.tagStack.append(tag)
  938.         self.currentTag = self.tagStack[-1]
  939.  
  940.     
  941.     def endData(self, containerClass = NavigableString):
  942.         if self.currentData:
  943.             currentData = ''.join(self.currentData)
  944.             if currentData.endswith('<') and self.convertHTMLEntities:
  945.                 currentData = currentData[:-1] + '<'
  946.             
  947.             if not currentData.strip():
  948.                 if '\n' in currentData:
  949.                     currentData = '\n'
  950.                 else:
  951.                     currentData = ' '
  952.             
  953.             self.currentData = []
  954.             if self.parseOnlyThese and len(self.tagStack) <= 1:
  955.                 if not (self.parseOnlyThese.text) or not self.parseOnlyThese.search(currentData):
  956.                     return None
  957.                 
  958.             o = containerClass(currentData)
  959.             o.setup(self.currentTag, self.previous)
  960.             if self.previous:
  961.                 self.previous.next = o
  962.             
  963.             self.previous = o
  964.             self.currentTag.contents.append(o)
  965.         
  966.  
  967.     
  968.     def _popToTag(self, name, inclusivePop = True):
  969.         if name == self.ROOT_TAG_NAME:
  970.             return None
  971.         
  972.         numPops = 0
  973.         mostRecentTag = None
  974.         for i in range(len(self.tagStack) - 1, 0, -1):
  975.             if name == self.tagStack[i].name:
  976.                 numPops = len(self.tagStack) - i
  977.                 break
  978.                 continue
  979.         
  980.         if not inclusivePop:
  981.             numPops = numPops - 1
  982.         
  983.         for i in range(0, numPops):
  984.             mostRecentTag = self.popTag()
  985.         
  986.         return mostRecentTag
  987.  
  988.     
  989.     def _smartPop(self, name):
  990.         nestingResetTriggers = self.NESTABLE_TAGS.get(name)
  991.         isNestable = nestingResetTriggers != None
  992.         isResetNesting = self.RESET_NESTING_TAGS.has_key(name)
  993.         popTo = None
  994.         inclusive = True
  995.         for i in range(len(self.tagStack) - 1, 0, -1):
  996.             p = self.tagStack[i]
  997.             if (not p or p.name == name) and not isNestable:
  998.                 popTo = name
  999.                 break
  1000.             
  1001.             if (nestingResetTriggers != None or p.name in nestingResetTriggers or nestingResetTriggers == None) and isResetNesting and self.RESET_NESTING_TAGS.has_key(p.name):
  1002.                 popTo = p.name
  1003.                 inclusive = False
  1004.                 break
  1005.             
  1006.             p = p.parent
  1007.         
  1008.         if popTo:
  1009.             self._popToTag(popTo, inclusive)
  1010.         
  1011.  
  1012.     
  1013.     def unknown_starttag(self, name, attrs, selfClosing = 0):
  1014.         if self.quoteStack:
  1015.             attrs = ''.join(map((lambda .0: (x, y) = .0' %s="%s"' % (x, y)), attrs))
  1016.             self.currentData.append('<%s%s>' % (name, attrs))
  1017.             return None
  1018.         
  1019.         self.endData()
  1020.         if not self.isSelfClosingTag(name) and not selfClosing:
  1021.             self._smartPop(name)
  1022.         
  1023.         if self.parseOnlyThese and len(self.tagStack) <= 1:
  1024.             if self.parseOnlyThese.text or not self.parseOnlyThese.searchTag(name, attrs):
  1025.                 return None
  1026.             
  1027.         tag = Tag(self, name, attrs, self.currentTag, self.previous)
  1028.         if self.previous:
  1029.             self.previous.next = tag
  1030.         
  1031.         self.previous = tag
  1032.         self.pushTag(tag)
  1033.         if selfClosing or self.isSelfClosingTag(name):
  1034.             self.popTag()
  1035.         
  1036.         if name in self.QUOTE_TAGS:
  1037.             self.quoteStack.append(name)
  1038.             self.literal = 1
  1039.         
  1040.         return tag
  1041.  
  1042.     
  1043.     def unknown_endtag(self, name):
  1044.         if self.quoteStack and self.quoteStack[-1] != name:
  1045.             self.currentData.append('</%s>' % name)
  1046.             return None
  1047.         
  1048.         self.endData()
  1049.         self._popToTag(name)
  1050.         if self.quoteStack and self.quoteStack[-1] == name:
  1051.             self.quoteStack.pop()
  1052.             self.literal = len(self.quoteStack) > 0
  1053.         
  1054.  
  1055.     
  1056.     def handle_data(self, data):
  1057.         if self.convertHTMLEntities and data:
  1058.             if data[0] == '&':
  1059.                 data = self.BARE_AMPERSAND.sub('&', data)
  1060.             else:
  1061.                 data = data.replace('&', '&').replace('<', '<').replace('>', '>')
  1062.         
  1063.         self.currentData.append(data)
  1064.  
  1065.     
  1066.     def _toStringSubclass(self, text, subclass):
  1067.         self.endData()
  1068.         self.handle_data(text)
  1069.         self.endData(subclass)
  1070.  
  1071.     
  1072.     def handle_pi(self, text):
  1073.         if text[:3] == 'xml':
  1074.             text = "xml version='1.0' encoding='%SOUP-ENCODING%'"
  1075.         
  1076.         self._toStringSubclass(text, ProcessingInstruction)
  1077.  
  1078.     
  1079.     def handle_comment(self, text):
  1080.         self._toStringSubclass(text, Comment)
  1081.  
  1082.     
  1083.     def handle_charref(self, ref):
  1084.         if ref[0] == 'x':
  1085.             data = unichr(int(ref[1:], 16))
  1086.         else:
  1087.             data = unichr(int(ref))
  1088.         if data <= data:
  1089.             pass
  1090.         elif data <= u'\xc2\x9f':
  1091.             data = UnicodeDammit.subMSChar(chr(ord(data)), self.smartQuotesTo)
  1092.         elif not (self.convertHTMLEntities) and not (self.convertXMLEntities):
  1093.             data = '&#%s;' % ref
  1094.         
  1095.         self.handle_data(data)
  1096.  
  1097.     
  1098.     def handle_entityref(self, ref):
  1099.         if self.convertXMLEntities:
  1100.             pass
  1101.         replaceWithXMLEntity = self.XML_ENTITIES_TO_CHARS.has_key(ref)
  1102.         if self.convertHTMLEntities or replaceWithXMLEntity:
  1103.             
  1104.             try:
  1105.                 data = unichr(name2codepoint[ref])
  1106.             except KeyError:
  1107.                 if replaceWithXMLEntity:
  1108.                     data = self.XML_ENTITIES_TO_CHARS.get(ref)
  1109.                 else:
  1110.                     data = '&%s' % ref
  1111.             except:
  1112.                 replaceWithXMLEntity
  1113.             
  1114.  
  1115.         None<EXCEPTION MATCH>KeyError
  1116.         data = '&%s;' % ref
  1117.         self.handle_data(data)
  1118.  
  1119.     
  1120.     def handle_decl(self, data):
  1121.         self._toStringSubclass(data, Declaration)
  1122.  
  1123.     
  1124.     def parse_declaration(self, i):
  1125.         j = None
  1126.         if self.rawdata[i:i + 9] == '<![CDATA[':
  1127.             k = self.rawdata.find(']]>', i)
  1128.             if k == -1:
  1129.                 k = len(self.rawdata)
  1130.             
  1131.             data = self.rawdata[i + 9:k]
  1132.             j = k + 3
  1133.             self._toStringSubclass(data, CData)
  1134.         else:
  1135.             
  1136.             try:
  1137.                 j = SGMLParser.parse_declaration(self, i)
  1138.             except SGMLParseError:
  1139.                 toHandle = self.rawdata[i:]
  1140.                 self.handle_data(toHandle)
  1141.                 j = i + len(toHandle)
  1142.  
  1143.         return j
  1144.  
  1145.  
  1146.  
  1147. class BeautifulSoup(BeautifulStoneSoup):
  1148.     
  1149.     def __init__(self, *args, **kwargs):
  1150.         if not kwargs.has_key('smartQuotesTo'):
  1151.             kwargs['smartQuotesTo'] = self.HTML_ENTITIES
  1152.         
  1153.         BeautifulStoneSoup.__init__(self, *args, **kwargs)
  1154.  
  1155.     SELF_CLOSING_TAGS = buildTagMap(None, [
  1156.         'br',
  1157.         'hr',
  1158.         'input',
  1159.         'img',
  1160.         'meta',
  1161.         'spacer',
  1162.         'link',
  1163.         'frame',
  1164.         'base'])
  1165.     QUOTE_TAGS = {
  1166.         'script': None }
  1167.     NESTABLE_INLINE_TAGS = [
  1168.         'span',
  1169.         'font',
  1170.         'q',
  1171.         'object',
  1172.         'bdo',
  1173.         'sub',
  1174.         'sup',
  1175.         'center']
  1176.     NESTABLE_BLOCK_TAGS = [
  1177.         'blockquote',
  1178.         'div',
  1179.         'fieldset',
  1180.         'ins',
  1181.         'del']
  1182.     NESTABLE_LIST_TAGS = {
  1183.         'ol': [],
  1184.         'ul': [],
  1185.         'li': [
  1186.             'ul',
  1187.             'ol'],
  1188.         'dl': [],
  1189.         'dd': [
  1190.             'dl'],
  1191.         'dt': [
  1192.             'dl'] }
  1193.     NESTABLE_TABLE_TAGS = {
  1194.         'table': [],
  1195.         'tr': [
  1196.             'table',
  1197.             'tbody',
  1198.             'tfoot',
  1199.             'thead'],
  1200.         'td': [
  1201.             'tr'],
  1202.         'th': [
  1203.             'tr'],
  1204.         'thead': [
  1205.             'table'],
  1206.         'tbody': [
  1207.             'table'],
  1208.         'tfoot': [
  1209.             'table'] }
  1210.     NON_NESTABLE_BLOCK_TAGS = [
  1211.         'address',
  1212.         'form',
  1213.         'p',
  1214.         'pre']
  1215.     RESET_NESTING_TAGS = buildTagMap(None, NESTABLE_BLOCK_TAGS, 'noscript', NON_NESTABLE_BLOCK_TAGS, NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS)
  1216.     NESTABLE_TAGS = buildTagMap([], NESTABLE_INLINE_TAGS, NESTABLE_BLOCK_TAGS, NESTABLE_LIST_TAGS, NESTABLE_TABLE_TAGS)
  1217.     CHARSET_RE = re.compile('((^|;)\\s*charset=)([^;]*)')
  1218.     
  1219.     def start_meta(self, attrs):
  1220.         httpEquiv = None
  1221.         contentType = None
  1222.         contentTypeIndex = None
  1223.         tagNeedsEncodingSubstitution = False
  1224.         for i in range(0, len(attrs)):
  1225.             (key, value) = attrs[i]
  1226.             key = key.lower()
  1227.             if key == 'http-equiv':
  1228.                 httpEquiv = value
  1229.                 continue
  1230.             if key == 'content':
  1231.                 contentType = value
  1232.                 contentTypeIndex = i
  1233.                 continue
  1234.         
  1235.         if httpEquiv and contentType:
  1236.             match = self.CHARSET_RE.search(contentType)
  1237.             if match:
  1238.                 if getattr(self, 'declaredHTMLEncoding') or self.originalEncoding == self.fromEncoding:
  1239.                     newAttr = self.CHARSET_RE.sub((lambda match: match.group(1) + '%SOUP-ENCODING%'), value)
  1240.                     attrs[contentTypeIndex] = (attrs[contentTypeIndex][0], newAttr)
  1241.                     tagNeedsEncodingSubstitution = True
  1242.                 else:
  1243.                     newCharset = match.group(3)
  1244.                     if newCharset and newCharset != self.originalEncoding:
  1245.                         self.declaredHTMLEncoding = newCharset
  1246.                         self._feed(self.declaredHTMLEncoding)
  1247.                         raise StopParsing
  1248.                     
  1249.             
  1250.         
  1251.         tag = self.unknown_starttag('meta', attrs)
  1252.         if tag and tagNeedsEncodingSubstitution:
  1253.             tag.containsSubstitutions = True
  1254.         
  1255.  
  1256.  
  1257.  
  1258. class StopParsing(Exception):
  1259.     pass
  1260.  
  1261.  
  1262. class ICantBelieveItsBeautifulSoup(BeautifulSoup):
  1263.     I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS = [
  1264.         'em',
  1265.         'big',
  1266.         'i',
  1267.         'small',
  1268.         'tt',
  1269.         'abbr',
  1270.         'acronym',
  1271.         'strong',
  1272.         'cite',
  1273.         'code',
  1274.         'dfn',
  1275.         'kbd',
  1276.         'samp',
  1277.         'strong',
  1278.         'var',
  1279.         'b',
  1280.         'big']
  1281.     I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS = [
  1282.         'noscript']
  1283.     NESTABLE_TAGS = buildTagMap([], BeautifulSoup.NESTABLE_TAGS, I_CANT_BELIEVE_THEYRE_NESTABLE_BLOCK_TAGS, I_CANT_BELIEVE_THEYRE_NESTABLE_INLINE_TAGS)
  1284.  
  1285.  
  1286. class MinimalSoup(BeautifulSoup):
  1287.     RESET_NESTING_TAGS = buildTagMap('noscript')
  1288.     NESTABLE_TAGS = { }
  1289.  
  1290.  
  1291. class BeautifulSOAP(BeautifulStoneSoup):
  1292.     
  1293.     def popTag(self):
  1294.         if len(self.tagStack) > 1:
  1295.             tag = self.tagStack[-1]
  1296.             parent = self.tagStack[-2]
  1297.             parent._getAttrMap()
  1298.             if isinstance(tag, Tag) and len(tag.contents) == 1 and isinstance(tag.contents[0], NavigableString) and not parent.attrMap.has_key(tag.name):
  1299.                 parent[tag.name] = tag.contents[0]
  1300.             
  1301.         
  1302.         BeautifulStoneSoup.popTag(self)
  1303.  
  1304.  
  1305.  
  1306. class BeautifulClosedSoup(BeautifulSoup):
  1307.     
  1308.     def isSelfClosingTag(self, name):
  1309.         return True
  1310.  
  1311.  
  1312.  
  1313. class RobustXMLParser(BeautifulStoneSoup):
  1314.     pass
  1315.  
  1316.  
  1317. class RobustHTMLParser(BeautifulSoup):
  1318.     pass
  1319.  
  1320.  
  1321. class RobustWackAssHTMLParser(ICantBelieveItsBeautifulSoup):
  1322.     pass
  1323.  
  1324.  
  1325. class RobustInsanelyWackAssHTMLParser(MinimalSoup):
  1326.     pass
  1327.  
  1328.  
  1329. class SimplifyingSOAPParser(BeautifulSOAP):
  1330.     pass
  1331.  
  1332.  
  1333. try:
  1334.     import chardet
  1335. except:
  1336.     chardet = None
  1337.  
  1338. chardet = None
  1339.  
  1340. try:
  1341.     import cjkcodecs.aliases as cjkcodecs
  1342. except:
  1343.     pass
  1344.  
  1345.  
  1346. try:
  1347.     import iconv_codec
  1348. except:
  1349.     pass
  1350.  
  1351.  
  1352. class UnicodeDammit:
  1353.     CHARSET_ALIASES = {
  1354.         'macintosh': 'mac-roman',
  1355.         'x-sjis': 'shift-jis' }
  1356.     
  1357.     def __init__(self, markup, overrideEncodings = [], smartQuotesTo = 'xml'):
  1358.         (self.markup, documentEncoding, sniffedEncoding) = self._detectEncoding(markup)
  1359.         self.smartQuotesTo = smartQuotesTo
  1360.         self.triedEncodings = []
  1361.         if isinstance(markup, unicode):
  1362.             return markup
  1363.         
  1364.         u = None
  1365.         for proposedEncoding in overrideEncodings:
  1366.             u = self._convertFrom(proposedEncoding)
  1367.             if u:
  1368.                 break
  1369.                 continue
  1370.         
  1371.         if not u:
  1372.             for proposedEncoding in (documentEncoding, sniffedEncoding):
  1373.                 u = self._convertFrom(proposedEncoding)
  1374.                 if u:
  1375.                     break
  1376.                     continue
  1377.             
  1378.         
  1379.         if not u and chardet and not isinstance(self.markup, unicode):
  1380.             u = self._convertFrom(chardet.detect(self.markup)['encoding'])
  1381.         
  1382.         if not u:
  1383.             for proposed_encoding in ('utf-8', 'windows-1252'):
  1384.                 u = self._convertFrom(proposed_encoding)
  1385.                 if u:
  1386.                     break
  1387.                     continue
  1388.             
  1389.         
  1390.         self.unicode = u
  1391.         if not u:
  1392.             self.originalEncoding = None
  1393.         
  1394.  
  1395.     
  1396.     def subMSChar(orig, smartQuotesTo):
  1397.         sub = UnicodeDammit.MS_CHARS.get(orig)
  1398.         if type(sub) == types.TupleType:
  1399.             if smartQuotesTo == 'xml':
  1400.                 sub = '&#x%s;' % sub[1]
  1401.             elif smartQuotesTo == 'html':
  1402.                 sub = '&%s;' % sub[0]
  1403.             else:
  1404.                 sub = unichr(int(sub[1], 16))
  1405.         
  1406.         return sub
  1407.  
  1408.     subMSChar = staticmethod(subMSChar)
  1409.     
  1410.     def _convertFrom(self, proposed):
  1411.         proposed = self.find_codec(proposed)
  1412.         if not proposed or proposed in self.triedEncodings:
  1413.             return None
  1414.         
  1415.         self.triedEncodings.append(proposed)
  1416.         markup = self.markup
  1417.         if self.smartQuotesTo and proposed in ('windows-1252', 'ISO-8859-1', 'ISO-8859-2'):
  1418.             markup = (re.compile('([\x80-\x9f])').sub,)((lambda x: self.subMSChar(x.group(1), self.smartQuotesTo)), markup)
  1419.         
  1420.         
  1421.         try:
  1422.             u = self._toUnicode(markup, proposed)
  1423.             self.markup = u
  1424.             self.originalEncoding = proposed
  1425.         except Exception:
  1426.             e = None
  1427.             return None
  1428.  
  1429.         return self.markup
  1430.  
  1431.     
  1432.     def _toUnicode(self, data, encoding):
  1433.         if len(data) >= 4 and data[:2] == '\xfe\xff' and data[2:4] != '\x00\x00':
  1434.             encoding = 'utf-16be'
  1435.             data = data[2:]
  1436.         elif len(data) >= 4 and data[:2] == '\xff\xfe' and data[2:4] != '\x00\x00':
  1437.             encoding = 'utf-16le'
  1438.             data = data[2:]
  1439.         elif data[:3] == '\xef\xbb\xbf':
  1440.             encoding = 'utf-8'
  1441.             data = data[3:]
  1442.         elif data[:4] == '\x00\x00\xfe\xff':
  1443.             encoding = 'utf-32be'
  1444.             data = data[4:]
  1445.         elif data[:4] == '\xff\xfe\x00\x00':
  1446.             encoding = 'utf-32le'
  1447.             data = data[4:]
  1448.         
  1449.         newdata = unicode(data, encoding)
  1450.         return newdata
  1451.  
  1452.     
  1453.     def _detectEncoding(self, xml_data):
  1454.         xml_encoding = None
  1455.         sniffed_xml_encoding = None
  1456.         
  1457.         try:
  1458.             if xml_data[:4] == 'Lo\xa7\x94':
  1459.                 xml_data = self._ebcdic_to_ascii(xml_data)
  1460.             elif xml_data[:4] == '\x00<\x00?':
  1461.                 sniffed_xml_encoding = 'utf-16be'
  1462.                 xml_data = unicode(xml_data, 'utf-16be').encode('utf-8')
  1463.             elif len(xml_data) >= 4 and xml_data[:2] == '\xfe\xff' and xml_data[2:4] != '\x00\x00':
  1464.                 sniffed_xml_encoding = 'utf-16be'
  1465.                 xml_data = unicode(xml_data[2:], 'utf-16be').encode('utf-8')
  1466.             elif xml_data[:4] == '<\x00?\x00':
  1467.                 sniffed_xml_encoding = 'utf-16le'
  1468.                 xml_data = unicode(xml_data, 'utf-16le').encode('utf-8')
  1469.             elif len(xml_data) >= 4 and xml_data[:2] == '\xff\xfe' and xml_data[2:4] != '\x00\x00':
  1470.                 sniffed_xml_encoding = 'utf-16le'
  1471.                 xml_data = unicode(xml_data[2:], 'utf-16le').encode('utf-8')
  1472.             elif xml_data[:4] == '\x00\x00\x00<':
  1473.                 sniffed_xml_encoding = 'utf-32be'
  1474.                 xml_data = unicode(xml_data, 'utf-32be').encode('utf-8')
  1475.             elif xml_data[:4] == '<\x00\x00\x00':
  1476.                 sniffed_xml_encoding = 'utf-32le'
  1477.                 xml_data = unicode(xml_data, 'utf-32le').encode('utf-8')
  1478.             elif xml_data[:4] == '\x00\x00\xfe\xff':
  1479.                 sniffed_xml_encoding = 'utf-32be'
  1480.                 xml_data = unicode(xml_data[4:], 'utf-32be').encode('utf-8')
  1481.             elif xml_data[:4] == '\xff\xfe\x00\x00':
  1482.                 sniffed_xml_encoding = 'utf-32le'
  1483.                 xml_data = unicode(xml_data[4:], 'utf-32le').encode('utf-8')
  1484.             elif xml_data[:3] == '\xef\xbb\xbf':
  1485.                 sniffed_xml_encoding = 'utf-8'
  1486.                 xml_data = unicode(xml_data[3:], 'utf-8').encode('utf-8')
  1487.             else:
  1488.                 sniffed_xml_encoding = 'ascii'
  1489.             xml_encoding_match = re.compile('^<\\?.*encoding=[\'"](.*?)[\'"].*\\?>').match(xml_data)
  1490.         except:
  1491.             xml_encoding_match = None
  1492.  
  1493.         if xml_encoding_match:
  1494.             xml_encoding = xml_encoding_match.groups()[0].lower()
  1495.             if sniffed_xml_encoding and xml_encoding in ('iso-10646-ucs-2', 'ucs-2', 'csunicode', 'iso-10646-ucs-4', 'ucs-4', 'csucs4', 'utf-16', 'utf-32', 'utf_16', 'utf_32', 'utf16', 'u16'):
  1496.                 xml_encoding = sniffed_xml_encoding
  1497.             
  1498.         
  1499.         return (xml_data, xml_encoding, sniffed_xml_encoding)
  1500.  
  1501.     
  1502.     def find_codec(self, charset):
  1503.         if not self._codec(self.CHARSET_ALIASES.get(charset, charset)):
  1504.             if not charset or self._codec(charset.replace('-', '')):
  1505.                 if not charset or self._codec(charset.replace('-', '_')):
  1506.                     pass
  1507.         return charset
  1508.  
  1509.     
  1510.     def _codec(self, charset):
  1511.         if not charset:
  1512.             return charset
  1513.         
  1514.         codec = None
  1515.         
  1516.         try:
  1517.             codecs.lookup(charset)
  1518.             codec = charset
  1519.         except LookupError:
  1520.             pass
  1521.  
  1522.         return codec
  1523.  
  1524.     EBCDIC_TO_ASCII_MAP = None
  1525.     
  1526.     def _ebcdic_to_ascii(self, s):
  1527.         c = self.__class__
  1528.         if not c.EBCDIC_TO_ASCII_MAP:
  1529.             emap = (0, 1, 2, 3, 156, 9, 134, 127, 151, 141, 142, 11, 12, 13, 14, 15, 16, 17, 18, 19, 157, 133, 8, 135, 24, 25, 146, 143, 28, 29, 30, 31, 128, 129, 130, 131, 132, 10, 23, 27, 136, 137, 138, 139, 140, 5, 6, 7, 144, 145, 22, 147, 148, 149, 150, 4, 152, 153, 154, 155, 20, 21, 158, 26, 32, 160, 161, 162, 163, 164, 165, 166, 167, 168, 91, 46, 60, 40, 43, 33, 38, 169, 170, 171, 172, 173, 174, 175, 176, 177, 93, 36, 42, 41, 59, 94, 45, 47, 178, 179, 180, 181, 182, 183, 184, 185, 124, 44, 37, 95, 62, 63, 186, 187, 188, 189, 190, 191, 192, 193, 194, 96, 58, 35, 64, 39, 61, 34, 195, 97, 98, 99, 100, 101, 102, 103, 104, 105, 196, 197, 198, 199, 200, 201, 202, 106, 107, 108, 109, 110, 111, 112, 113, 114, 203, 204, 205, 206, 207, 208, 209, 126, 115, 116, 117, 118, 119, 120, 121, 122, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 123, 65, 66, 67, 68, 69, 70, 71, 72, 73, 232, 233, 234, 235, 236, 237, 125, 74, 75, 76, 77, 78, 79, 80, 81, 82, 238, 239, 240, 241, 242, 243, 92, 159, 83, 84, 85, 86, 87, 88, 89, 90, 244, 245, 246, 247, 248, 249, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 250, 251, 252, 253, 254, 255)
  1530.             import string
  1531.             c.EBCDIC_TO_ASCII_MAP = string.maketrans(''.join(map(chr, range(256))), ''.join(map(chr, emap)))
  1532.         
  1533.         return s.translate(c.EBCDIC_TO_ASCII_MAP)
  1534.  
  1535.     MS_CHARS = {
  1536.         '\x80': ('euro', '20AC'),
  1537.         '\x81': ' ',
  1538.         '\x82': ('sbquo', '201A'),
  1539.         '\x83': ('fnof', '192'),
  1540.         '\x84': ('bdquo', '201E'),
  1541.         '\x85': ('hellip', '2026'),
  1542.         '\x86': ('dagger', '2020'),
  1543.         '\x87': ('Dagger', '2021'),
  1544.         '\x88': ('circ', '2C6'),
  1545.         '\x89': ('permil', '2030'),
  1546.         '\x8a': ('Scaron', '160'),
  1547.         '\x8b': ('lsaquo', '2039'),
  1548.         '\x8c': ('OElig', '152'),
  1549.         '\x8d': '?',
  1550.         '\x8e': ('#x17D', '17D'),
  1551.         '\x8f': '?',
  1552.         '\x90': '?',
  1553.         '\x91': ('lsquo', '2018'),
  1554.         '\x92': ('rsquo', '2019'),
  1555.         '\x93': ('ldquo', '201C'),
  1556.         '\x94': ('rdquo', '201D'),
  1557.         '\x95': ('bull', '2022'),
  1558.         '\x96': ('ndash', '2013'),
  1559.         '\x97': ('mdash', '2014'),
  1560.         '\x98': ('tilde', '2DC'),
  1561.         '\x99': ('trade', '2122'),
  1562.         '\x9a': ('scaron', '161'),
  1563.         '\x9b': ('rsaquo', '203A'),
  1564.         '\x9c': ('oelig', '153'),
  1565.         '\x9d': '?',
  1566.         '\x9e': ('#x17E', '17E'),
  1567.         '\x9f': ('Yuml', '178') }
  1568.  
  1569.  
  1570. try:
  1571.     import psyco
  1572. except ImportError:
  1573.     pass
  1574.  
  1575. for obj in [
  1576.     PageElement,
  1577.     NavigableString,
  1578.     CData,
  1579.     ProcessingInstruction,
  1580.     Comment,
  1581.     Declaration,
  1582.     Tag,
  1583.     SoupStrainer,
  1584.     ResultSet,
  1585.     BeautifulStoneSoup,
  1586.     BeautifulSoup,
  1587.     BeautifulSOAP,
  1588.     UnicodeDammit]:
  1589.     psyco.bind(obj)
  1590.  
  1591. if __name__ == '__main__':
  1592.     import sys
  1593.     soup = BeautifulSoup(sys.stdin.read())
  1594.     print soup.prettify()
  1595.  
  1596.